﻿using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Comdiv.QWeb.Utils;

namespace Comdiv.QWeb.Serialization.BxlParser {
	public class BxlToken {
		//NOTE:not used
		/*public override string ToString()
		{
			StringBuilder sb = new StringBuilder();
			sb.Append(Type);
			if(SubType!=BxlTokenType.None) {
				sb.Append("::" + SubType);
			}
			if(!string.IsNullOrWhiteSpace(Value)) {
				sb.Append("=" + Value);
			}
			
			return sb.ToString();
		}*/
		private static readonly char[] digits = new[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

		private static readonly char[] nonnames = new[]
		                                          	{
		                                          		'+', '-', '?', '!', '~', '@', '*', '$', '^', '&'
		                                          		, '/', ':', '%', '(', ')', '{', '}', '[', ']', '<', '>'
		                                          	};

		public string AttrValue;
		public bool ColonPrefixed;
		public int CurrentNestLevel;
		public BxlToken ElementValue;
		public bool Ignore;
		public BxlToken Impl;
		public bool IsAssigner;
		public BxlToken Left;
		public int Level;

		public BxlLexInfo LexInfo;
		public string Name;
		public decimal Number;
		public BxlToken Parent;
		public BxlToken Right;
		public BxlTokenType SubType; //for opened constructions
		public BxlTokenType Type;
		public string Value;
		private IList<BxlToken> _childAttributes;
		private IList<BxlToken> _childElements;
		private BxlToken _next;

		private BxlToken _prev;
		private XName adaptedname;

		public BxlToken(BxlTokenType type) {
			Type = type;
		}

		public BxlToken() {
		}

		public BxlToken Next {
			get { return _next; }
			set {
				if (_next == value) return;
				if (null != _next) _next._prev = null;
				if (null != value) value._prev = this;
				_next = value;
			}
		}


		public BxlToken Prev {
			get { return _prev; }
			set {
				if (null != _prev) _prev._next = null;
				if (null != value) value._next = this;
				_prev = value;
			}
		}

		public IList<BxlToken> Elements {
			get { return _childElements ?? (_childElements = new List<BxlToken>()); }
		}

		public IList<BxlToken> Attributes {
			get { return _childAttributes ?? (_childAttributes = new List<BxlToken>()); }
		}

		public BxlToken GetNext(int count) {
			var current = Next;
			var c = count;
			while (current != null && c > 0) {
				c--;
				current = current.Next;
			}
			return current;
		}

		public BxlToken GetPrev(int count) {
			var current = Prev;
			var c = count;
			while (current != null && c > 0) {
				c--;
				current = current.Prev;
			}
			return current;
		}

		public XName GetAdaptedName(IDictionary<string, string> namespaces = null) {
			if (null == adaptedname) {
				if (Name.Contains("::")) {
					if (Name.EndsWith("::")) {
						throw new BxlParserException("illegal namespace declaration", LexInfo);
					}
					var nameparts = Name.split(false, true, ':');
					if (nameparts.Count == 2) {
						var localname = ToXmlName(nameparts[1]);
						var prefix = nameparts[0];
						if (namespaces == null || !namespaces.ContainsKey(prefix)) {
							throw new BxlParserException("unknown ns prefix", LexInfo);
						}
						adaptedname = "{" + namespaces[prefix] + "}" + localname;
					}
					else {
						adaptedname = ToXmlName(Name);
					}
				}
				else {
					adaptedname = ToXmlName(Name);
				}
			}
			return adaptedname;
		}

		public static string ToXmlName(string s) {
			var _adaptedname = s;
			if (-1 != _adaptedname.IndexOfAny(nonnames)) {
				_adaptedname = _adaptedname
					.Replace("+", "__PLUS__")
					.Replace("-", "__MINUS__")
					.Replace("?", "__ASK__")
					.Replace("!", "__EXC__")
					.Replace("~", "__TILD__")
					.Replace("@", "__AT__")
					.Replace("*", "__STAR__")
					.Replace("$", "__USD__")
					.Replace("^", "__UP__")
					.Replace("&", "__AMP__")
					.Replace("/", "__DIV__")
					.Replace(":", "__DBL__")
					.Replace("%", "__PERC__")
					.Replace("(", "__LBRACE__")
					.Replace(")", "__RBRACE__")
					.Replace("[", "__LINDEX__")
					.Replace("]", "__RINDEX__")
					.Replace("{", "__LBLOCK__")
					.Replace("}", "__RBLOCK__")
					.Replace("|", "__VLINE__")
					.Replace(";", "__PERIOD__")
					.Replace("<", "__LT__")
					.Replace(">", "__GT__")
					;
			}
			if (0 != _adaptedname.Length && -1 != Array.IndexOf(digits, _adaptedname[0])) {
				_adaptedname = "_" + _adaptedname;
			}
			return _adaptedname;
		}

		public string ToString(int length) {
			var result = Value;
			var c = length;
			var current = Next;
			while (c > 0 && current != null) {
				c--;
				result += current.Value;
				current = current.Next;
			}
			return result;
		}

		public BxlToken Standalone() {
			var copy = new BxlToken
			           	{
			           		Type = Type,
			           		Value = Value,
			           		LexInfo = LexInfo.Clone(),
			           		CurrentNestLevel = CurrentNestLevel,
			           	};
			return copy;
		}
	}
}